/*
 * Param.cpp
 *
 *  Created on: Dec 17, 2014
 *      Author: Phong
 */

#include "Param.h"

Param* Param::share(){
    refCount++;
    return this;
}

void Param::free(Param *param) {
    param->refCount--;
    if (param->refCount == 0) delete param;
}

Param::Param (int dim, int wdim, int nwords, int npos, int ndeprel, real r1, real r2) {
	// common i/o
	rootInner = Matrix::Matrix::uniform(dim, -r2, r2); weights.push_back(rootInner);
	anonOuter = Matrix::Matrix::uniform(dim, -r2, r2); weights.push_back(anonOuter);
	anonInner[0] = Matrix::uniform(dim, -r2, r2); weights.push_back(anonInner[0]);
	anonInner[1] = Matrix::uniform(dim, -r2, r2); weights.push_back(anonInner[1]);

	// project wembs onto higher dimension space
	Ww = Matrix::uniform(dim, wdim, -r1, r1); weights.push_back(Ww);
	bw = Matrix::uniform(dim, 0, 0); weights.push_back(bw);

	// for computing inner
	Wih = Matrix::uniform(dim, dim, -r1, r1); weights.push_back(Wih);
	bi = Matrix::uniform(dim, 0, 0); weights.push_back(bi);
	for (int dir = 0; dir < 2; dir++) {
		for (int dr = 0; dr < ndeprel; dr++) {
			Matrix* w = Matrix::uniform(dim, dim, -r1, r1);
			Wi[dir].push_back(w);
			weights.push_back(w);
		}
	}

	// for computing outer
	for (int dir = 0; dir < 2; dir++) {
        Woh[dir] = Matrix::uniform(dim, dim, -r1, r1); weights.push_back(Woh[dir]);
        Wop[dir] = Matrix::uniform(dim, dim, -r1, r1); weights.push_back(Wop[dir]);
        bo[dir] = Matrix::uniform(dim, 0, 0); weights.push_back(bo[dir]);
		for (int dr = 0; dr < ndeprel; dr++) {
			Matrix* w = Matrix::uniform(dim, dim, -r1, r1);
			Wo[dir].push_back(w);
			weights.push_back(w);
		}
	}

	// for computing softmaxs
	Wdr = Matrix::uniform(ndeprel+1, dim, -r2, r2); weights.push_back(Wdr);
	bdr = Matrix::uniform(ndeprel+1, 0, 0); weights.push_back(bdr);

	Wpos = Matrix::uniform(npos, dim, -r2, r2); weights.push_back(Wpos);
	Wdrpos = Matrix::uniform(npos, ndeprel, -r2, r2); weights.push_back(Wdrpos);
	bpos = Matrix::uniform(npos, 0, 0); weights.push_back(bpos);

	for (int i = 0; i < nwords; i++)
		Wword.push_back(Matrix::uniform(dim, -r2, r2)); // push_back it later
	Wdrword = Matrix::uniform(nwords, ndeprel, -r2, r2); weights.push_back(Wdrword);
	Wposword = Matrix::uniform(nwords, npos, -r2, r2); weights.push_back(Wposword);
	bword = Matrix::uniform(nwords, 0, 0); weights.push_back(bword);

	Wcap = Matrix::uniform(N_CAP_FEAT, dim, -r2, r2); weights.push_back(Wcap);
	Wdrcap = Matrix::uniform(N_CAP_FEAT, ndeprel, -r2, r2); weights.push_back(Wdrcap);
	Wposcap = Matrix::uniform(N_CAP_FEAT, npos, -r2, r2); weights.push_back(Wposcap);
	Wwordcap = Matrix::uniform(N_CAP_FEAT, nwords, -r2, r2); weights.push_back(Wwordcap);
	bcap = Matrix::uniform(N_CAP_FEAT, 0, 0); weights.push_back(bcap);

	// for embeddings
	for (int i = 0; i < npos; i++) {
		Matrix* w = Matrix::uniform(dim, -r2, r2);
		Lpos.push_back(w);
		weights.push_back(w);
	}

	for (int i = 0; i < N_CAP_FEAT; i++) {
		Matrix* w = Matrix::uniform(dim, -r2, r2);
		Lcap.push_back(w);
		weights.push_back(w);
	}
	nMatricesWoWE = (int)weights.size();

	for (int i = 0; i < nwords; i++) weights.push_back(Wword[i]);

	for (int i = 0; i < nwords; i++) {
		Matrix* w = Matrix::uniform(wdim, -r2, r2);
		L.push_back(w);
		weights.push_back(w);
	}
}

void Param::save(ofstream &f) {
    f << nMatricesWoWE << endl;
    
    Ww->save(f); bw->save(f);
    
    rootInner->save(f); anonOuter->save(f);
    anonInner[0]->save(f); anonInner[1]->save(f);
    
    Wih->save(f); bi->save(f);
    f << Wi[0].size() << endl;
    for (int i = 0; i < Wi[0].size(); i++) {
        Wi[0][i]->save(f);
        Wi[1][i]->save(f);
    }
    
    Woh[0]->save(f); Woh[1]->save(f);
    Wop[0]->save(f); Wop[1]->save(f);
    bo[0]->save(f); bo[1]->save(f);
    f << Wo[0].size() << endl;
    for (int i = 0; i < Wo[0].size(); i++) {
        Wo[0][i]->save(f);
        Wo[1][i]->save(f);
    }

    Wdr->save(f); bdr->save(f);
    Wpos->save(f); Wdrpos->save(f); bpos->save(f);
    
    f << Wword.size() << endl;
    for (int i = 0; i < Wword.size(); i++)
        Wword[i]->save(f);
    Wdrword->save(f); Wposword->save(f); bword->save(f);
    
    Wcap->save(f); Wdrcap->save(f); Wposcap->save(f); Wwordcap->save(f); bcap->save(f);
    
    f << Lpos.size() << endl;
    for (int i = 0; i < Lpos.size(); i++)
        Lpos[i]->save(f);
    f << Lcap.size() << endl;
    for (int i = 0; i < Lcap.size(); i++)
        Lcap[i]->save(f);
    f << L.size() << endl;
    for (int i = 0; i < L.size(); i++)
        L[i]->save(f);
    
    f << endl;
}

Param* Param::load(ifstream &f) {
    Param* param = new Param();
    int n;
    
    f >> param->nMatricesWoWE;
    
    param->Ww = Matrix::load(f); param->bw = Matrix::load(f);
    
    param->rootInner = Matrix::load(f); param->anonOuter = Matrix::load(f);
    param->anonInner[0] = Matrix::load(f); param->anonInner[1] = Matrix::load(f);
    
    param->Wih = Matrix::load(f); param->bi = Matrix::load(f);
    f >> n;
    for (int i = 0; i < n; i++) {
        param->Wi[0].push_back(Matrix::load(f));
        param->Wi[1].push_back(Matrix::load(f));
    }
    
    param->Woh[0] = Matrix::load(f); param->Woh[1] = Matrix::load(f);
    param->Wop[0] = Matrix::load(f); param->Wop[1] = Matrix::load(f);
    param->bo[0] = Matrix::load(f); param->bo[1] = Matrix::load(f);
    f >> n;
    for (int i = 0; i < n; i++) {
        param->Wo[0].push_back(Matrix::load(f));
        param->Wo[1].push_back(Matrix::load(f));
    }
    
    param->Wdr = Matrix::load(f); param->bdr = Matrix::load(f);
    param->Wpos = Matrix::load(f); param->Wdrpos = Matrix::load(f); param->bpos = Matrix::load(f);
    
    f >> n;
    for (int i = 0; i < n; i++)
        param->Wword.push_back(Matrix::load(f));
    param->Wdrword = Matrix::load(f); param->Wposword = Matrix::load(f); param->bword = Matrix::load(f);
    
    param->Wcap = Matrix::load(f); param->Wdrcap = Matrix::load(f);
    param->Wposcap = Matrix::load(f); param->Wwordcap = Matrix::load(f); param->bcap = Matrix::load(f);
    
    f >> n;
    for (int i = 0; i < n; i++)
        param->Lpos.push_back(Matrix::load(f));
    f >> n;
    for (int i = 0; i < n; i++)
        param->Lcap.push_back(Matrix::load(f));
    f >> n;
    for (int i = 0; i < n; i++)
        param->L.push_back(Matrix::load(f));
    
    return param;
}

void Param::fill(real value) {
	for (int i = 0; i < weights.size(); i++) {
		weights[i]->fill(value);
	}
}

Param::~Param() {
	for (int i = 0; i < weights.size(); i++) {
		delete weights[i];
	}
}
